import json
from pathlib import Path
from typing import Any, Dict

import matplotlib.pyplot as plt
import networkx as nx
import seaborn as sns


class GraphComparator:
    def __init__(self, gold_graph_path: str, current_graph_path: str):
        """初始化比较器

        Args:
            gold_graph_path: 原始完整执行图的路径
            current_graph_path: 当前适配执行图的路径
        """
        self.gold_data = self._load_graph(gold_graph_path)
        self.current_data = self._load_graph(current_graph_path)

        # 构建NetworkX图
        self.gold_graph = nx.node_link_graph(self.gold_data)
        self.current_graph = nx.node_link_graph(self.current_data)

    def _load_graph(self, path: str) -> Dict:
        """加载图数据"""
        with open(path, 'r') as f:
            return json.load(f)

    def compare_execution_paths(self) -> Dict[str, Any]:
        """比较执行路径"""

        # 找到出度为0的节点（终点节点）
        def get_terminal_nodes(graph):
            return [node for node in graph.nodes() if graph.out_degree(node) == 0]

        try:
            # 获取起点和终点
            gold_start = (
                list(self.gold_graph.nodes())[0] if self.gold_graph.nodes() else None
            )
            gold_terminals = get_terminal_nodes(self.gold_graph)

            current_start = (
                list(self.current_graph.nodes())[0]
                if self.current_graph.nodes()
                else None
            )
            current_terminals = get_terminal_nodes(self.current_graph)
            # 打印终点节点信息
            # print("\n终点节点统计:")
            # print(f"Gold图终点数量: {len(gold_terminals)}")
            # print("Gold图终点节点:")
            # for node in gold_terminals:
            #     print(f"  - {node}")

            # print(f"\nCurrent图终点数量: {len(current_terminals)}")
            # print("Current图终点节点:")
            # for node in current_terminals:
            #     print(f"  - {node}")
            if not gold_start or not current_start:
                return {
                    'total_gold_paths': 0,
                    'total_current_paths': 0,
                    'matched_paths': [],
                    'average_coverage': 0,
                }

            # 获取所有路径
            gold_paths = []
            for terminal in gold_terminals:
                paths = list(nx.all_simple_paths(self.gold_graph, gold_start, terminal))
                gold_paths.extend(paths)

            current_paths = []
            for terminal in current_terminals:
                paths = list(
                    nx.all_simple_paths(self.current_graph, current_start, terminal)
                )
                current_paths.extend(paths)

            # 计算路径匹配度
            path_similarities = []  # 存储每条gold路径的最佳匹配相似度
            matched_paths = []

            # 打印路径统计信息
            # print("\n执行路径统计:")
            # print(f"Gold图路径总数: {len(gold_paths)}")
            # print(f"Current图路径总数: {len(current_paths)}")

            # # 打印每条Gold路径的详细信息
            # print("\nGold图路径详情:")
            # for i, path in enumerate(gold_paths):
            #     print(f"\n路径 {i+1}:")
            #     for node in path:
            #         print(f"  - {node}")

            for gold_path in gold_paths:
                # 对每条gold路径，找到最匹配的current路径
                path_similarity = 0
                best_match = None

                for current_path in current_paths:
                    similarity = calculate_path_similarity(
                        gold_path, current_path, self.gold_graph, self.current_graph
                    )

                    if similarity > path_similarity:
                        path_similarity = similarity
                        best_match = current_path

                if best_match is not None:
                    path_similarities.append(path_similarity)
                    matched_paths.append(
                        {
                            'gold_path': gold_path,
                            'current_path': best_match,
                            'similarity': path_similarity,
                        }
                    )

            # 计算平均相似度：使用所有gold路径的最佳匹配相似度的平均值
            average_similarity = (
                sum(path_similarities) / len(gold_paths) if gold_paths else 0
            )

            return {
                'total_gold_paths': len(gold_paths),
                'total_current_paths': len(current_paths),
                'matched_paths': matched_paths,
                'average_coverage': average_similarity,
            }
        except Exception as e:
            print(f'警告: 计算执行路径时出错: {str(e)}')
            return {
                'total_gold_paths': 0,
                'total_current_paths': 0,
                'matched_paths': [],
                'average_coverage': 0,
            }

    def compare_modules(self) -> Dict[str, Dict[str, float]]:
        """比较模块级别的执行情况"""

        def get_module_stats(graph, execution_status):
            module_stats = {}
            for node in graph.nodes():
                module = graph.nodes[node].get('module', '').split('.')[0]
                if not module:
                    continue

                if module not in module_stats:
                    module_stats[module] = {'total': 0, 'completed': 0}

                module_stats[module]['total'] += 1
                if execution_status.get(node) == 'completed':
                    module_stats[module]['completed'] += 1

            return {
                module: {
                    'completion_rate': stats['completed'] / stats['total'] * 100,
                    'total': stats['total'],
                    'completed': stats['completed'],
                }
                for module, stats in module_stats.items()
            }

        gold_stats = get_module_stats(
            self.gold_graph, self.gold_data['execution_status']
        )
        current_stats = get_module_stats(
            self.current_graph, self.current_data['execution_status']
        )

        # 计算每个模块的适配进度
        progress = {}
        for module in gold_stats:
            if module in current_stats:
                gold = gold_stats[module]
                current = current_stats[module]
                progress[module] = {
                    'coverage': (current['total'] / gold['total'] * 100),
                    'completion': current['completion_rate'],
                    'gold_total': gold['total'],
                    'current_total': current['total'],
                    'gold_completed': gold['completed'],
                    'current_completed': current['completed'],
                }
            else:
                progress[module] = {
                    'coverage': 0,
                    'completion': 0,
                    'gold_total': gold_stats[module]['total'],
                    'current_total': 0,
                    'gold_completed': gold_stats[module]['completed'],
                    'current_completed': 0,
                }

        return progress

    def compare_execution_times(self) -> Dict[str, Any]:
        """比较执行时间"""
        common_nodes = set(self.gold_graph.nodes()) & set(self.current_graph.nodes())
        time_comparisons = {}

        for node in common_nodes:
            gold_time = self.gold_data['execution_times'].get(node, 0)
            current_time = self.current_data['execution_times'].get(node, 0)
            if gold_time > 0:  # 只比较在gold图中有执行时间的节点
                time_comparisons[node] = {
                    'gold_time': gold_time,
                    'current_time': current_time,
                    'difference': current_time - gold_time,
                    'ratio': current_time / gold_time
                    if gold_time > 0
                    else float('inf'),
                }

        return time_comparisons

    def calculate_graph_similarity(self) -> float:
        """计算图编辑距离（GED）相似度

        使用简化的GED计算方法：
        1. 节点操作：添加/删除节点的成本
        2. 边操作：添加/删除边的成本
        """
        # 获取节点和边的集合
        gold_nodes = set(self.gold_graph.nodes())
        current_nodes = set(self.current_graph.nodes())
        gold_edges = set(self.gold_graph.edges())
        current_edges = set(self.current_graph.edges())

        # 计算需要添加和删除的节点数量
        nodes_to_add = len(
            current_nodes - gold_nodes
        )  # 在current中有但gold中没有的节点
        nodes_to_delete = len(
            gold_nodes - current_nodes
        )  # 在gold中有但current中没有的节点

        # 计算需要添加和删除的边数量
        edges_to_add = len(current_edges - gold_edges)  # 在current中有但gold中没有的边
        edges_to_delete = len(
            gold_edges - current_edges
        )  # 在gold中有但current中没有的边

        # 计算总的编辑距离
        total_edit_distance = (
            nodes_to_add + nodes_to_delete + edges_to_add + edges_to_delete
        )

        # 计算最大可能的编辑距离（完全不同的图之间的距离）
        max_possible_distance = (
            len(gold_nodes) + len(current_nodes) + len(gold_edges) + len(current_edges)
        )

        # 如果两个图都是空的，返回100%相似度
        if max_possible_distance == 0:
            return 100.0

        # 将编辑距离转换为相似度百分比
        similarity = (1 - total_edit_distance / max_possible_distance) * 100

        return max(0.0, similarity)  # 确保相似度不会小于0

    def get_overall_progress(self) -> Dict[str, float]:
        """计算总体适配进度"""
        # 1. 节点覆盖率
        gold_nodes = set(self.gold_graph.nodes())
        current_nodes = set(self.current_graph.nodes())
        node_coverage = len(current_nodes & gold_nodes) / len(gold_nodes) * 100

        # 2. 边覆盖率
        gold_edges = set(self.gold_graph.edges())
        current_edges = set(self.current_graph.edges())
        edge_coverage = len(current_edges & gold_edges) / len(gold_edges) * 100

        # 3. 图结构相似度
        graph_similarity = self.calculate_graph_similarity()

        # 4. 执行路径覆盖（暂时注释掉）
        # path_analysis = self.compare_execution_paths()
        # path_coverage = path_analysis['average_coverage']

        # 计算权平均
        weights = {'node_coverage': 0.3, 'edge_coverage': 0.3, 'graph_similarity': 0.4}

        overall_progress = (
            node_coverage * weights['node_coverage']
            + edge_coverage * weights['edge_coverage']
            + graph_similarity * weights['graph_similarity']
        )

        return {
            'overall': overall_progress,
            'node_coverage': node_coverage,
            'edge_coverage': edge_coverage,
            'graph_similarity': graph_similarity,
            # 'path_coverage': path_coverage
        }

    def visualize_comparison(self, output_dir: str):
        """可视化比较结果"""
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        # 1. 模块进度条形图
        module_progress = self.compare_modules()
        plt.figure(figsize=(12, 6))
        modules = list(module_progress.keys())
        coverage = [m['coverage'] for m in module_progress.values()]
        completion = [m['completion'] for m in module_progress.values()]

        x = range(len(modules))
        width = 0.35

        plt.bar(
            [i - width / 2 for i in x],
            coverage,
            width,
            label='Coverage',
            color='blue',
            alpha=0.6,
        )
        plt.bar(
            [i + width / 2 for i in x],
            completion,
            width,
            label='Completion',
            color='green',
            alpha=0.6,
        )

        plt.xlabel('Modules')
        plt.ylabel('Percentage')
        plt.title('Module Progress Comparison')
        plt.xticks(x, modules, rotation=45)
        plt.legend()
        plt.tight_layout()
        plt.savefig(output_dir / 'module_progress.png')
        plt.close()

        # 2. 总体进度仪表盘
        progress = self.get_overall_progress()
        plt.figure(figsize=(10, 10))

        metrics = list(progress.keys())
        values = list(progress.values())

        colors = sns.color_palette('husl', len(metrics))
        plt.pie(values, labels=metrics, colors=colors, autopct='%1.1f%%')
        plt.title('Overall Adaptation Progress')
        plt.savefig(output_dir / 'overall_progress.png')
        plt.close()

        # 3. 保存详细报告
        report = {
            'overall_progress': progress,
            'module_progress': module_progress,
            'execution_paths': self.compare_execution_paths(),
            'execution_times': self.compare_execution_times(),
        }

        with open(output_dir / 'comparison_report.json', 'w') as f:
            json.dump(report, f, indent=2)


def calculate_path_similarity(gold_path, current_path, gold_graph, current_graph):
    """计算路径相似度，考虑��点顺序和边的关系"""
    # 1. 计算节点匹配度
    gold_nodes = set(gold_path)
    current_nodes = set(current_path)
    common_nodes = gold_nodes & current_nodes

    if not common_nodes:
        return 0.0

    node_coverage = len(common_nodes) / len(gold_nodes)

    # 2. 计算边匹配度
    gold_edges = set(zip(gold_path[:-1], gold_path[1:]))
    current_edges = set(zip(current_path[:-1], current_path[1:]))
    common_edges = gold_edges & current_edges

    edge_coverage = len(common_edges) / len(gold_edges) if gold_edges else 1.0

    # 3. 计算序列顺序相似度
    sequence_similarity = 0.0
    if len(common_nodes) > 1:
        # 获取共同节点在两个路径中的位置
        node_positions = {
            node: {'gold': gold_path.index(node), 'current': current_path.index(node)}
            for node in common_nodes
        }

        # 计算相对顺序的保持程度
        correct_order_pairs = 0
        total_pairs = 0
        nodes = list(common_nodes)

        for i in range(len(nodes)):
            for j in range(i + 1, len(nodes)):
                node1, node2 = nodes[i], nodes[j]
                gold_order = (
                    node_positions[node1]['gold'] < node_positions[node2]['gold']
                )
                current_order = (
                    node_positions[node1]['current'] < node_positions[node2]['current']
                )

                if gold_order == current_order:
                    correct_order_pairs += 1
                total_pairs += 1

        sequence_similarity = (
            correct_order_pairs / total_pairs if total_pairs > 0 else 1.0
        )

    # 综合三个指标，使用加权平均
    weights = {'node': 0.4, 'edge': 0.3, 'sequence': 0.3}

    similarity = (
        node_coverage * weights['node']
        + edge_coverage * weights['edge']
        + sequence_similarity * weights['sequence']
    ) * 100

    return similarity


def compare_two_graphs(gold_graph_path: str, current_graph_path: str) -> dict:
    """比较两个图的相似度

    Args:
        gold_graph_path: 原始完整执行图的JSON文件路径
        current_graph_path: 当前适配执行图的JSON文件路径

    Returns:
        dict: 包含比较结果的字典，包括：
            - overall: 总体适配进度
            - node_coverage: 节点覆盖率
            - edge_coverage: 边覆盖率
            - graph_similarity: 图结构相似度
    """
    # 创建比较器实例
    comparator = GraphComparator(gold_graph_path, current_graph_path)

    # 获取进度信息
    progress = comparator.get_overall_progress()

    return progress


def print_comparison_results(progress: dict):
    """打印比较结果

    Args:
        progress: 比较结果字典
    """
    print('\n图比较结果:')
    print(f"总体适配进度: {progress['overall']:.1f}%")
    print(f"节点覆盖率: {progress['node_coverage']:.1f}%")
    print(f"边覆盖率: {progress['edge_coverage']:.1f}%")
    print(f"图结构相似度: {progress['graph_similarity']:.1f}%")


def main():
    import argparse

    parser = argparse.ArgumentParser(description='Compare execution graphs')
    parser.add_argument('--gold', required=True, help='Path to the gold standard graph')
    parser.add_argument('--current', required=True, help='Path to the current graph')
    parser.add_argument(
        '--output', default='comparison_results', help='Output directory for results'
    )

    args = parser.parse_args()

    # 比较图并打印结果
    results = compare_two_graphs(args.gold, args.current)
    print_comparison_results(results)

    # 生成可视化结果
    comparator = GraphComparator(args.gold, args.current)
    comparator.visualize_comparison(args.output)


if __name__ == '__main__':
    main()
